package com.equis.architecture.util {
	
	
	import com.equis.architecture.preloader.AbstractBatchItem;
	
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.ProgressEvent;
	
	import mx.collections.ArrayList;
	import mx.collections.IList;
	import mx.core.FlexGlobals;
	
	/**
	 * Dispatched when finished but one or more of its items dispatched an ERROR event.
	 */
	[Event(name="error", type="flash.events.ErrorEvent")]
	[Event(name="progress", type="flash.events.ProgressEvent")]
	/**
	 * Dispatched when finished all items have cpompleted successfully.
	 */
	[Event(name="complete", type="flash.events.Event")]
	/**
	 * Dispatched after a call to the @see cancel method, when the currently running items are finished.
	 */
	[Event(name="cancel", type="flash.events.Event")]
	
	/**
	 * Allows for execution of a set of calls asynchronously.
	 */
	public class AsyncMultiCaller extends EventDispatcher {

	    private var batchItems:IList = new ArrayList();
	    
	    private var errorText:String;
	    
	    private var cancelRequest:Boolean = false;

		public function AsyncMultiCaller() {
			
		}

		/**
		 * Adds an @see ar.com.mindset.flexy.preloader.AbstractBatchItem to the batcher in order to be executed.
		 * @param index If 0, the added item will be the next to run, before any previous items. This can be useful for preemptive service calls.
		 */
		public function registerBatchItem(req:AbstractBatchItem, index:int = -1):void {
			if(index == -1) {
				batchItems.addItem(req);
			} else {
				batchItems.addItemAt(req, index);
			}
			pendingCalls++;
			totalCalls++;
		}

		protected var pendingCalls:Number = 0;
		protected var runningCalls:Number = 0;
		protected var totalCalls:Number = 0;
		
		public var maxCalls:Number = 5;
		
		public var running:Boolean = false;
		public var finished:Boolean = false;

        /**
         * Starts making the registered calls in order, no more than @see maxCalls simultaneously.
         */ 
		public function execute():void {
        	running = true;
        	pendingCalls++;
        	runningCalls++;
			callback();
        }
        
        /**
         * Requests the batcher stop processing.
         * After the last running item is finished, a CANCEL event is dispatched.
         */ 
        public function cancel():void {
        	cancelRequest = true;
        }

		private function doExecute():void {
			if(cancelRequest) {
				if(runningCalls == 0) {
		        	running = false;
		        	finished = true;
					dispatchEvent(new Event(Event.CANCEL));
				}
				return;
			}
			
			for each(var req:AbstractBatchItem in batchItems.toArray()) {
				if(!req.complete && !req.running) {
					if(req.isRequiredComplete() && runningCalls < maxCalls) {
						runningCalls++;
						req.call(callback);
					}
				}
			}
        }

        
		protected function callback(_errorText:String = null):void {
        	dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, 1 + (totalCalls - pendingCalls), totalCalls));
			pendingCalls--;
			runningCalls--;
        	if(_errorText != null) {
				this.errorText = _errorText;
        	} else {
        	}
            if(pendingCalls == 0) {
            	doFinish();
			} else {
				FlexGlobals.topLevelApplication.callLater(doExecute);
			}
        }
        
        protected function doFinish():void {
        	running = false;
        	finished = true;
        	if(errorText == null) {
                dispatchEvent(new Event(Event.COMPLETE));
        	} else {
                dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, errorText));
        	}
        }
       
        /**
         * Removes all pending items.
         */ 
		public function clearRequisites():void{
			this.pendingCalls = 0;
			this.totalCalls = 0;
			this.batchItems.removeAll();
		}
	}
}